#include <map>
#include <stack>

#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>

#include <osg/Transform>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osg/Transform>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/TriangleFunctor>

#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <BRep_Builder.hxx>
#include <BRepTools.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepLib_MakePolygon.hxx>
#include <BRepLib_MakeFace.hxx>
#include <GeomLib_IsPlanarSurface.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <BRepAdaptor_Surface.hxx>

#include "ActorTable.h"
#include "Registry.h"
#include "OsgUtil.h"
#include "OcctEditShape.h"
#include "Polygon.h"
#include "Surface.h"
#include "Scene.h"
#include "BrepJson.h"
#include "OcctOsg.h"
#include "FixShape.h"

struct LessVec3f
{
	static bool less(const osg::Vec3f& p1, const osg::Vec3f& p2)
	{
		if (!lengthApprox(p1.x(), p2.x()))
		{
			return p1.x() < p2.x();
		}
		if (!lengthApprox(p1.y(), p2.y()))
		{
			return p1.y() < p2.y();
		}
		if (!lengthApprox(p1.z(), p2.z()))
		{
			return p1.z() < p2.z();
		}
		return false;
	}

	bool operator()(const osg::Vec3f& p1, const osg::Vec3f& p2) const
	{
		return less(p1, p2);
	}
};

struct OsgVec3Edge
{
	osg::Vec3f p1;
	osg::Vec3f p2;

	bool operator==(const OsgVec3Edge& other) const
	{
		return pointApprox(p1, other.p1)
			&& pointApprox(p2, other.p2);
	}
};

struct ImportOcctCache
{
	std::map<osg::Vec3f, TopoDS_Vertex, LessVec3f> vertexCache;
	std::stack<osg::Matrix> matrice;
	std::vector<std::vector<osg::Vec3f>> triangles;

	std::vector<OsgVec3Edge> edges;
	bool adjustEdge = false;

	void pushMatrix(const osg::Matrix& matrix)
	{
		osg::Matrix current = getMatrix();
		osg::Matrix tmp = matrix * current;
		matrice.push(tmp);
	}

	void popMatrix()
	{
		matrice.pop();
	}

	osg::Matrix getMatrix()
	{
		if (matrice.empty())
			return osg::Matrix();
		return matrice.top();
	}

	TopoDS_Vertex getVertex(const osg::Vec3f& v1)
	{
		BRep_Builder builder;
		auto it = vertexCache.find(v1);
		TopoDS_Vertex tv1;
		if (it != vertexCache.end())
		{
			tv1 = it->second;
		}
		else
		{
			builder.MakeVertex(tv1, toPnt(v1), 0.0001);
			vertexCache.insert(std::make_pair(v1, tv1));
		}
		return tv1;
	}

	bool hasEdge(const OsgVec3Edge& e)
	{
		for (const auto edge : edges)
		{
			if (edge == e)
				return true;
		}
		return false;
	}
};

struct TriangleProcessor
{
	TriangleProcessor()
	{
	}

	void setCache(ImportOcctCache& cache)
	{
		m_cache = &cache;
	}

	~TriangleProcessor()
	{
	}

	void operator() (const osg::Vec3f& v1, const osg::Vec3f& v2, const osg::Vec3f& v3) const
	{
		auto M = m_cache->getMatrix();
		auto v1M = v1 * M;
		auto v2M = v2 * M;
		auto v3M = v3 * M;
		if (m_cache->adjustEdge)
		{
			bool exist = m_cache->hasEdge(OsgVec3Edge{ v1M, v2M });
			if (!exist)
				exist = m_cache->hasEdge(OsgVec3Edge{ v2M, v3M });
			if (!exist)
				exist = m_cache->hasEdge(OsgVec3Edge{ v3M, v1M });
			// 同一个边相邻的两个面，它们共享边的方向应该是相反的。
			if (exist)
			{
				std::swap(v2M, v3M);
			}
			m_cache->edges.push_back(OsgVec3Edge{ v1M, v2M });
			m_cache->edges.push_back(OsgVec3Edge{ v2M, v3M });
			m_cache->edges.push_back(OsgVec3Edge{ v3M, v1M });
		}
		m_cache->triangles.push_back({ v1M, v2M, v3M });
	}

protected:

	ImportOcctCache* m_cache;
};

void osg2OcctFace(const osg::Node* node, ImportOcctCache& cache, bool pushMatrix)
{
	const osg::Group* group = node->asGroup();
	if (group)
	{
		if (pushMatrix)
		{
			const osg::Transform* tf = node->asTransform();
			if (tf)
			{
				// 假设ReferenceFrame都是RELATIVE_RF，这应该是普遍情况
				osg::Matrixd matrix;
				tf->computeLocalToWorldMatrix(matrix, nullptr);
				cache.pushMatrix(matrix);
			}
		}
		int num = group->getNumChildren();
		for (int i = 0; i < num; ++i)
		{
			const osg::Node* child = group->getChild(i);
			osg2OcctFace(child, cache, true);
		}
		if (pushMatrix)
		{
			const osg::Transform* tf = node->asTransform();
			if (tf)
			{
				cache.popMatrix();
			}
		}
	}
	else
	{
		const osg::Geometry* geom = node->asGeometry();
		if (geom)
		{
			osg::TriangleFunctor<TriangleProcessor> tf;
			tf.setCache(cache);
			geom->accept(tf);
		}
	}
}

#if false
// 合并多边形和三角形的算法
bool mergeCoplanarPolygonTriangle(std::vector<osg::Vec3f>& polygon, const std::vector<osg::Vec3f>& triangle)
{
	for (int i = 0; i < polygon.size(); ++i)
	{
		for (int j = 0; j < triangle.size()/*应该是3*/; ++j)
		{
			// 寻找相同的顶点
			if (pointApprox(polygon[i], triangle[j]))
			{
				int nextI = (i + 1);
				if (nextI == polygon.size())
					nextI = 0;
				int prevJ = (j - 1);
				if (prevJ < 0)
					prevJ = triangle.size() - 1;
				// 寻找第二个相同的顶点
				if (pointApprox(polygon[nextI], triangle[prevJ]))
				{
					// 找到了多边形和三角形的共同边
					// 下面判断多边形和三角形是否共面，共面就合并
					auto d1P = polygon[1] - polygon[0];
					auto d2P = polygon[2] - polygon[1];
					auto dirP = d1P ^ d2P;
					auto d1T = triangle[1] - triangle[0];
					auto d2T = triangle[2] - triangle[1];
					auto dirT = d1T ^ d2T;
					dirP.normalize();
					dirT.normalize();
					if (dirApprox(dirP, dirT)) // 两个法线是否相同，法线相同那么共面
					{
						int nextJ = j + 1;
						if (nextJ == triangle.size())
							nextJ = 0;
						polygon.insert(polygon.begin() + nextI, triangle[nextJ]);
						return true;
					}
					else
					{
						return false;
					}
				}
			}
		}
	}
	return false;
}

//  合并连续共面的三角形
void mergeContiguousCoplanarTriangles(const std::vector<std::vector<osg::Vec3f>>& triangles, std::vector<std::vector<osg::Vec3f>>& polygons)
{
	std::vector<osg::Vec3f> polygon;
	for (int index = 0; index < (int)triangles.size(); index++)
	{
		const std::vector<osg::Vec3f>& triangle = triangles[index];
		if (index == 0)
		{
			polygon = triangle;
		}
		else
		{
			bool merged = mergeCoplanarPolygonTriangle(polygon, triangle);
			if (merged)
			{
				// polygon已经是合并后的多边形
			}
			else
			{
				polygons.push_back(polygon);
				polygon = triangle;
			}
		}
	}
	if (!polygon.empty())
		polygons.push_back(polygon);
}
#endif

// 合并多边形和三角形的算法
bool mergePolygonTriangle(std::vector<osg::Vec3f>& polygon, const std::vector<osg::Vec3f>& triangle)
{
	for (int i = 0; i < polygon.size(); ++i)
	{
		for (int j = 0; j < triangle.size()/*应该是3*/; ++j)
		{
			// 寻找相同的顶点
			if (pointApprox(polygon[i], triangle[j]))
			{
				int nextI = (i + 1);
				if (nextI == polygon.size())
					nextI = 0;
				int prevJ = (j - 1);
				if (prevJ < 0)
					prevJ = triangle.size() - 1;
				// 寻找第二个相同的顶点
				if (pointApprox(polygon[nextI], triangle[prevJ]))
				{
					// 找到了多边形和三角形的第一个共同边
					int nextJ = j + 1;
					if (nextJ == triangle.size())
						nextJ = 0;
					int prevI = (i - 1);
					if (prevI < 0)
						prevI = polygon.size() - 1;
					if (pointApprox(polygon[prevI], triangle[nextJ]))
					{
						// 多边形和三角形有二个共同边
						polygon.erase(polygon.begin() + i);
					}
					else
					{
						// 多边形和三角形只有一个共同边
						polygon.insert(polygon.begin() + nextI, triangle[nextJ]);
					}
					return true;
					}
				}
			}
		}
	return false;
	}

//  合并共面的三角形
void mergeCoplanarTriangles(const std::vector<std::vector<osg::Vec3f>>& triangles, std::vector<std::vector<osg::Vec3f>>& polygons)
{
	// 1. 把三角形按照平面分组。
	// 注意，不能使用std::map，某些情况下插入会失败，原因不明，似乎和小于比较函数有关。
	using PlaneTrianglesPair = std::pair<PlaneBase, std::vector<std::vector<osg::Vec3f>>>;
	std::vector<PlaneTrianglesPair> planeTrianglesVector;
	for (int index = 0; index < (int)triangles.size(); ++index)
	{
		const auto& triangle = triangles[index];
		PlaneBase plane;
		bool success = getPlane(triangle[0], triangle[1], triangle[2], plane);
		if (!success)
			continue;

		auto it = std::find_if(planeTrianglesVector.begin(), planeTrianglesVector.end(), [&plane](const PlaneTrianglesPair& pair) { return isSamePlane(plane, pair.first); });
		if (it == planeTrianglesVector.end())
		{
			planeTrianglesVector.push_back(std::make_pair(plane, std::vector<std::vector<osg::Vec3f>>{triangle}));
		}
		else
		{
			it->second.push_back(triangle);
		}
	}

	// 2. 每组三角形按照共同边合并，直到没有共同边为止，生成一个多边形。如果三角形组不为空，继续前面的过程。
	for (auto& sameDirTriangles : planeTrianglesVector)
	{
		while (!sameDirTriangles.second.empty())
		{
			std::vector<osg::Vec3f> polygon = sameDirTriangles.second[0];
			sameDirTriangles.second.erase(sameDirTriangles.second.begin());
			std::vector<int> mergedNums;
			do
			{
				mergedNums.clear();
				for (int index = 0; index < (int)sameDirTriangles.second.size(); ++index)
				{
					const std::vector<osg::Vec3f>& triangle = sameDirTriangles.second[index];
					bool merged = mergePolygonTriangle(polygon, triangle);
					if (merged)
					{
						mergedNums.push_back(index);
					}
				}
				std::vector<std::vector<osg::Vec3f>> remainTriangles;
				std::vector<bool> remainIndice(sameDirTriangles.second.size(), true);
				for (int index = 0; index < (int)mergedNums.size(); ++index)
				{
					remainIndice[mergedNums[index]] = false;
				}
				for (int index = 0; index < (int)remainIndice.size(); ++index)
				{
					if (remainIndice[index])
						remainTriangles.push_back(sameDirTriangles.second[index]);
				}
				sameDirTriangles.second.swap(remainTriangles);
			} while (!mergedNums.empty() && !sameDirTriangles.second.empty());
			polygons.push_back(polygon);
		}
	}
}

TopoDS_Face polygonToFace(const std::vector<osg::Vec3f>& polygon, ImportOcctCache& cache)
{
	BRepLib_MakePolygon wireBuilder;
	for (const auto& vertex : polygon)
	{
		TopoDS_Vertex tv = cache.getVertex(vertex);
		wireBuilder.Add(tv);
	}
	wireBuilder.Close();
	BRepLib_MakeFace faceBuilder(wireBuilder.Wire(), true);
	auto face = faceBuilder.Face();
	return face;
}

std::vector<TopoDS_Face> osg2OcctFace(osg::Node* node, bool merge)
{
	std::vector<TopoDS_Face> faces;
	ImportOcctCache cache;
	osg2OcctFace(node, cache, false);
	if (cache.triangles.empty())
		return faces;
	std::vector<std::vector<osg::Vec3f>> polygons;
	std::vector<std::vector<osg::Vec3f>> nonplanarPolygons;
	//mergeContiguousCoplanarTriangles(cache.result, result);
	if (merge)
	{
		mergeCoplanarTriangles(cache.triangles, polygons);
	}
	else
	{
		polygons = cache.triangles;
	}
	mergeCoplanarTriangles(cache.triangles, polygons);
	while (true)
	{
		for (const auto& polygon : polygons)
		{
			// Convert polygon to TopoDS_Face
			try
			{
				auto face = polygonToFace(polygon, cache);
				if (!face.IsNull())
				{
					faces.push_back(face);
				}
				else
				{
					nonplanarPolygons.push_back(polygon);
				}
			}
			catch (...)
			{
				// 有时候polygon的点完全一样，这时polygonToFace>BRepLib_MakeFace会抛出异常
				// do nothing
			}
		}
		if (nonplanarPolygons.empty())
			break;
		polygons.clear();
		for (auto& polygon : nonplanarPolygons)
		{
			auto last = polygon.back();
			polygon.pop_back();
			polygons.push_back(polygon);
			std::vector<osg::Vec3f> triangle;
			triangle.push_back(polygon.back());
			triangle.push_back(last);
			triangle.push_back(polygon.front());
			polygons.push_back(triangle);
		}
		nonplanarPolygons.clear();
	}
	return faces;
}

osg::ref_ptr<osg::Node> removeTopTransform(osg::ref_ptr<osg::Node> node)
{
	if (!node)
		return nullptr;
	const osg::Group* group = node->asGroup();
	while (group)
	{
		const osg::Transform* trans = group->asTransform();
		if (trans)
		{
			osg::Group* tmp = new osg::Group();
			int num = trans->getNumChildren();
			for (int i = 0; i < num; ++i)
				tmp->addChild(const_cast<osg::Node*>(trans->getChild(i)));
			return tmp;
		}
		else
		{
			if (group->getNumChildren() == 1)
			{
				auto child = group->getChild(0);
				auto tmp = child->asGroup();
				if (tmp)
				{
					group = tmp;
					continue;
				}
			}
			return (osg::Node*)group;
		}
	}
	return node;
}

std::set<std::shared_ptr<INode>> importModel(Scene* scene, const QString& filePath, double scale, bool merge)
{
	Registry &reg = Registry::instance();
	std::set<std::shared_ptr<INode>> result;
	BrepJson instance;
	if (filePath.endsWith(".brep.json", Qt::CaseInsensitive) ||
		filePath.endsWith(".brepjson", Qt::CaseInsensitive))
	{
		instance.load(filePath.toStdWString());
	}
	else if (filePath.endsWith(".step", Qt::CaseInsensitive) ||
		filePath.endsWith(".stp", Qt::CaseInsensitive))
	{
		auto con = loadStep(filePath.toStdString());
		if (con)
			instance.addConstruct(con);
	}
	else if (filePath.endsWith(".iges", Qt::CaseInsensitive) ||
		filePath.endsWith(".igs", Qt::CaseInsensitive))
	{
		bool stackoverflow;
		auto con = loadIges(filePath.toStdString(), stackoverflow);
		if (con)
			instance.addConstruct(con);
		else
		{
			if (stackoverflow)
			{
				QMessageBox::critical(reg.get<QWidget>(mainWindowID),
					QObject::tr("错误"), QObject::tr("发生了栈溢出，请保存文件后尽快退出程序"));
			}
		}
	}
	else if (filePath.endsWith(".brep", Qt::CaseInsensitive) ||
		filePath.endsWith(".brp", Qt::CaseInsensitive))
	{
		auto con = loadBrep(filePath.toStdString());
		if (con)
			instance.addConstruct(con);
	}
	else
	{
		std::string filePath2(filePath.toLocal8Bit().data());
		osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(filePath2);
		if (!node)
			return result;
		node = removeTopTransform(node);
		std::vector<TopoDS_Face> faces = osg2OcctFace(node, merge);
		if (!faces.empty())
		{
			auto con = std::make_shared<ConstructData>();
			for (const auto& face: faces)
			{
				con->addFace(face);
			}
			instance.addConstruct(con);
		}
	}

	gp_Trsf trsf;
	if (scale != 1.0)
		trsf.SetScale(gp_Pnt(), scale);
	for (const auto& conData : instance.getConstructs())
	{
		// 包围盒检测，拒绝过大的构件，不然面和边的离散化会很耗时
		osg::Vec3d minCorner, maxCorner;
		bool success = conData->getBound(minCorner, maxCorner);
		if (success)
		{
			if (maxCorner.x() - minCorner.x() <= 2000 / scale &&
				maxCorner.y() - minCorner.y() <= 2000 / scale &&
				maxCorner.z() - minCorner.z() <= 2000 / scale)
			{
			}
			else
			{
				Registry &reg = Registry::instance();
				QMessageBox::warning(reg.get<QWidget>(mainWindowID), QObject::tr("错误"), QObject::tr("构件尺寸过大，建议使用厘米/毫米导入"));
				continue;
			}
		}
		for (const auto& face2 : conData->getFaces())
		{
			auto myFace = std::make_shared<Surface>(scene);
			TopoDS_Face face;
			fixFaceAndEdges(face2, face);
			TopoDS_Face scaledFaceShape = TopoDS::Face(trsfShape(face, trsf));
			auto surface = BRep_Tool::Surface(scaledFaceShape);
			GeomLib_IsPlanarSurface checker(surface);
			if (checker.IsPlanar())
			{
				BRepAdaptor_Surface s1(scaledFaceShape);
				auto type = s1.GetType();
				if (type == GeomAbs_Plane)
				{
					myFace->setShape(scaledFaceShape);
				}
				else
				{
					BRepLib_MakeFace maker(checker.Plan());
					TopExp_Explorer Ex(scaledFaceShape, TopAbs_WIRE);
					while (Ex.More()) {
						maker.Add(TopoDS::Wire(Ex.Current()));
						Ex.Next();
					}
					if (maker.IsDone())
						myFace->setShape(maker.Face());
					else
						myFace->setShape(scaledFaceShape);
				}
			}
			else
			{
				myFace->setShape(scaledFaceShape);
			}
			result.insert(myFace);
		}
		// 暂时只支持导入一个构件，忽略位置和旋转
		break;
	}
	return result;
}
